package org.dfl.core.model;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dfl.core.model.transitions.Transition;
import org.dfl.core.model.transitions.TransitionFactory;
import org.dfl.messages.Messages;
import org.jdom.Element;
import org.jdom.Namespace;


/**
 * model of a flow - container for all places, edges and transitions
 * Currently this class is not used because there is a new one Diagram.class in a plugin
 * @author Piotr Wlodarczyk
 *
 */
public class Model implements IOElement{
	// to sa mapy zamiast kolekcji TYLKO na potrzeby wczytywania!
	// po wczytaniu korzystac z tego TYLKO jako z kolekcji, bo idiki moga sie zmieniac,
	// a nie sa one zmieniane w tych mapach!
	private Map<String,Place> places = new HashMap<String,Place>();
	private Map<String,Transition> transitions = new HashMap<String,Transition>();
	private Map<String,Edge> edges = new HashMap<String,Edge>();
	
	private InputPlace input;
	private OutputPlace output;
	
	public void setInputPlace(InputPlace place){
		input = place;
	}
	public void setOutputPlace(OutputPlace place){
		output = place;
	}
	
	public InputPlace getInput() {
		return input;
	}
	public OutputPlace getOutput() {
		return output;
	}

	//places
	public Map<String,Place> getPlaces() {
		return places;
	}
	void setPlaces(Map<String,Place> places) {
		this.places = places;
	}
	public void addPlace(Place place){
		places.put(place.getId(), place);
	}
	//transitions
	public Map<String,Transition> getTransitions() {
		return transitions;
	}
	void setTransitions(Map<String,Transition> transitions) {
		this.transitions = transitions;
	}
	public void addTransition(Transition transition){
		transitions.put(transition.getId(), transition);
	}
	
	//	edges
	public Map<String,Edge> getEdges() {
		return edges;
	}
	void setEdges(Map<String,Edge> edges) {
		this.edges = edges;
	}
	public void addEdge(Edge e){
		edges.put(e.getId(), e);
	}
	
	/** 
	 * "odpala" model 
	 * @param inputData
	 * @return
	 * @throws TransitionExecutionException
	 */
	public Object run(Object inputData) throws TransitionExecutionException{
		return null;
		// TODO implement run method here
		
	}

	/**
	 * wszelkie sprawdzenia, czy model jest ok
	 */
	public void  checkModel() throws Exception{
		// czy ogolnie jest ok
		if(getInput()==null || getOutput()==null || getPlaces()==null || getTransitions()==null){
			throw new Exception(Messages.Model_modelDefined);
		}
		
		// sprawdzenie, czy kazda tranzycja ma zdefiniowane wszystkie wejscia
		Iterator<String> iter = transitions.keySet().iterator();
		while(iter.hasNext()){
			Transition t = transitions.get((String) iter.next());
			if(!t.areAllInputsDefined()){
				throw new Exception(Messages.Model_errorNotAllTransitionInputs1+t.getName()+Messages.Model_errorNotAllTransitionInputs2);
			}
		}
	}
	
	@SuppressWarnings("unchecked") //$NON-NLS-1$
	public void load(Element root) throws XMLLoaderException {
		try {
			Namespace namespace = root.getNamespace();
			
			Element netElement = root.getChild("net"); //$NON-NLS-1$
			String inputPlaceId = netElement.getChild("toolspecific").getChildText("inputPlace", namespace); //$NON-NLS-1$ //$NON-NLS-2$
			String outputPlaceId = netElement.getChild("toolspecific").getChildText("outputPlace", namespace); //$NON-NLS-1$ //$NON-NLS-2$
			
			loadPlaces(inputPlaceId, outputPlaceId, netElement.getChildren("place"),namespace); //$NON-NLS-1$
			loadTransitions(netElement.getChildren("transition"),namespace); //$NON-NLS-1$
			loadEdges(netElement.getChildren("arc"),namespace); //$NON-NLS-1$
			
			checkModel();
		} catch (Exception e) {
			throw new XMLLoaderException(e);
			// maybe the document is not well-formed, or it's not valid
			// or some other reasons.
		}
	}
	
	private void loadTransitions(List<Element> children, Namespace namespace) throws Exception {
		Iterator<Element> tElemIter = children.iterator();
		while(tElemIter.hasNext()){
			Element tElem = (Element) tElemIter.next();
			Transition t = TransitionFactory.loadTransition(tElem, namespace);			
			addTransition(t);
		}
	}

	private void loadPlaces(String inputPlaceId, String outputPlaceId, List<Element> children, Namespace namespace) throws XMLLoaderException {
		Iterator<Element> pElemIter = children.iterator();
		
		while(pElemIter.hasNext()){
			Element pElem = (Element) pElemIter.next();
			String id = pElem.getAttributeValue("id",namespace); //$NON-NLS-1$
			
			Place p = null;
			if(id.equals(inputPlaceId)){
				p = new InputPlace();
			}else if(id.equals(outputPlaceId)){
				p = new OutputPlace();
			}else{
				p = new Place();	
			}
			p.load(pElem);
			addPlace(p);
		}
		
		setInputPlace((InputPlace) places.get(inputPlaceId));
		setOutputPlace((OutputPlace) places.get(outputPlaceId));
	}
	
	private void loadEdges(List<Element> children, Namespace namespace) throws Exception {
		//first load only transition inputs
		for(Object elem : children ){
			Element eElem = (Element) elem;
			
			String inputId = eElem.getAttributeValue("source",namespace); //$NON-NLS-1$
			String outputId = eElem.getAttributeValue("target",namespace); //$NON-NLS-1$
			
			if(places.containsKey(inputId)){
				// inputem jest miejsce
				Edge e = new TInputEdge();
				e.load(eElem);
				e.connect(places.get(inputId),transitions.get(outputId));
				addEdge(e);
				
			}
		}
		
		// then load transition outputs
		for(Object elem : children ){
			Element eElem = (Element) elem;
			
			String inputId = eElem.getAttributeValue("source",namespace); //$NON-NLS-1$
			String outputId = eElem.getAttributeValue("target",namespace); //$NON-NLS-1$
			
			if(transitions.containsKey(inputId)){
				// inputem jest tranzycja
				Edge e = new TOutputEdge();
				e.load(eElem);
				e.connect(places.get(outputId),transitions.get(inputId));
				addEdge(e);
			}		
		}
	}
	
	@SuppressWarnings("unchecked") //$NON-NLS-1$
	public Element save(){
		Element root = new Element("pnml"); //$NON-NLS-1$
		Element net = new Element("net"); //$NON-NLS-1$
		net.setAttribute("type",""); //$NON-NLS-1$ //$NON-NLS-2$
		net.setAttribute("id","pn"); //$NON-NLS-1$ //$NON-NLS-2$
		root.addContent(net);
		
		// zapisanie id wejscia i wyjscia
		Element tsElem = new Element("toolspecific"); //$NON-NLS-1$
		tsElem.setAttribute("tool",""); //$NON-NLS-1$ //$NON-NLS-2$
		tsElem.setAttribute("version",""); //$NON-NLS-1$ //$NON-NLS-2$
		net.addContent(tsElem);
		Element in = new Element("inputPlace"); //$NON-NLS-1$
		in.setText(getInput().getId());
		tsElem.addContent(in);
		Element out = new Element("outputPlace"); //$NON-NLS-1$
		out.setText(getOutput().getId());
		tsElem.addContent(out);
		
		Iterator iter = null;
		//miejsca
		for(iter = places.values().iterator();iter.hasNext();){
			net.addContent(((Place)iter.next()).save());
		}
		
		//tranzycje
		for(iter = transitions.values().iterator();iter.hasNext();){
			net.addContent(((Transition)iter.next()).save());
		}
		
		//polaczenia
		for(iter = edges.values().iterator();iter.hasNext();){
			net.addContent(((Edge)iter.next()).save());
		}
		return root;
	}
}

